import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from sklearn.decomposition import PCA
import platform

# Set font based on operating system
if platform.system() == 'Windows':
    plt.rcParams['font.sans-serif'] = ['SimHei']  # Windows common Chinese font
elif platform.system() == 'Darwin':  # macOS
    plt.rcParams['font.sans-serif'] = ['STHeiti']
else:  # Linux or other
    plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']  # Example font, may vary based on system

# Set minus sign to display correctly
plt.rcParams['axes.unicode_minus'] = False

# Generate 3D synthetic data
def generate_3d_data(n_samples=100):
    np.random.seed(42)
    mean = [5, 5, 5]
    cov = [[3, 1, 0.5], [1, 2, 1], [0.5, 1, 1.5]]  # Covariance matrix
    X = np.random.multivariate_normal(mean, cov, n_samples)
    return X

# Animate PCA projection from 3D to 1D
def animate_pca_3d_to_1d(X):
    # Fit PCA to the data
    pca = PCA(n_components=1)
    pca.fit(X)
    principal_component = pca.components_[0]
    explained_variance = pca.explained_variance_ratio_[0]

    # Project data onto the first principal component (1D line)
    projected_points = np.dot(X - X.mean(axis=0), principal_component.reshape(-1, 1)) * principal_component + X.mean(axis=0)

    # Set up the 3D plot
    fig = plt.figure(figsize=(10, 8))
    ax = fig.add_subplot(111, projection='3d')
    ax.scatter(X[:, 0], X[:, 1], X[:, 2], alpha=0.6, label="原始数据点")
    ax.set_title("PCA 高维 (3D → 1D 投影)")
    ax.set_xlabel("特征 1")
    ax.set_ylabel("特征 2")
    ax.set_zlabel("特征 3")

    # Plot the principal component line
    mean_point = X.mean(axis=0)
    line_length = 10  # Adjust for line visibility
    line_start = mean_point - line_length * principal_component
    line_end = mean_point + line_length * principal_component
    ax.plot([line_start[0], line_end[0]], [line_start[1], line_end[1]], [line_start[2], line_end[2]], 'r', label=f"主成分 1 ({explained_variance:.2%} 方差)")

    # Animate the projection of each point onto the principal component
    for i in range(len(X)):
        # Draw line from the original point to the projected point on the principal component
        ax.plot([X[i, 0], projected_points[i, 0]], 
                [X[i, 1], projected_points[i, 1]], 
                [X[i, 2], projected_points[i, 2]], 
                'gray', linestyle="--", alpha=0.5)
        # Plot the projected point on the principal component line
        ax.scatter(projected_points[i, 0], projected_points[i, 1], projected_points[i, 2], color="orange", alpha=0.7)
        
        plt.draw()
        plt.pause(0.1)  # Adjust for animation speed

    ax.legend()
    plt.show()

if __name__ == "__main__":
    # Generate data and animate PCA 3D to 1D projection
    X = generate_3d_data(n_samples=100)
    animate_pca_3d_to_1d(X)